package com.matrixxun.pulltozoomlistsimple;

import android.app.Activity;
import android.content.Context;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;

public class PullToZoomListView extends ListView implements AbsListView.OnScrollListener {

    private static final int    INVALID_VALUE = -1;
    private static final String TAG           = "PullToZoomListView";

    private static final Interpolator sInterpolator = new Interpolator() {

        public float getInterpolation(float anonymousFloat) {
            float f = anonymousFloat - 1.0F;
            return 1.0F + f * (f * (f * (f * f)));
        }
    };

    private FrameLayout mHeaderContainer;
    private ImageView   mHeaderImage;
    private ImageView   mShadow;

    private int mHeaderHeight;
    private int mScreenHeight;

    private int mActivePointerId = -1;

    private float mLastMotionY = -1.0F;
    private float mLastScale   = -1.0F;
    private float mMaxScale    = -1.0F;

    private OnScrollListener  mOnScrollListener;
    private ScalingRunnalable mScalingRunnalable;

    public PullToZoomListView(Context context) {
        super(context);
        init(context);
    }

    public PullToZoomListView(Context context, AttributeSet attr) {
        super(context, attr);
        init(context);
    }

    public PullToZoomListView(Context context, AttributeSet attr, int defaultValue) {
        super(context, attr, defaultValue);
        init(context);
    }

    private void endScraling() {
        if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {
            Log.d(TAG, "endScraling");
        }
        this.mScalingRunnalable.startAnimation(200L);
    }

    private void init(Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics);

        this.mScreenHeight = metrics.heightPixels;

        this.mHeaderContainer = new FrameLayout(context);
        this.mHeaderImage = new ImageView(context);

        int width = metrics.widthPixels;
        int headerHeight = (int) (9.0F * (width / 16.0F));

        setHeaderViewSize(width, headerHeight);

        this.mShadow = new ImageView(context);

        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
        params.gravity = Gravity.BOTTOM;

        this.mShadow.setLayoutParams(params);
        this.mHeaderContainer.addView(this.mHeaderImage);
        this.mHeaderContainer.addView(this.mShadow);

        addHeaderView(this.mHeaderContainer);

        this.mScalingRunnalable = new ScalingRunnalable();

        super.setOnScrollListener(this);
    }

    private void onSecondaryPointerUp(MotionEvent event) {
        int i = (event.getAction()) >> 8;
        if (event.getPointerId(i) == this.mActivePointerId) {
            if (i != 0) {
                this.mLastMotionY = event.getY(0);
                this.mActivePointerId = event.getPointerId(0);

                return;
            }
        }
    }

    private void reset() {
        this.mActivePointerId = -1;
        this.mLastMotionY = -1.0F;
        this.mMaxScale = -1.0F;
        this.mLastScale = -1.0F;
    }

    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        if (this.mHeaderHeight == 0) {
            this.mHeaderHeight = this.mHeaderContainer.getHeight();
        }

        Log.i(TAG, "onLayout() mHeaderContainer.getHeight()=" + mHeaderHeight);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        Log.d(TAG, "onScroll");

        float f = this.mHeaderHeight - this.mHeaderContainer.getBottom();

        Log.d(TAG, "f|" + f);

        if ((f > 0.0F) && (f < this.mHeaderHeight)) {
            Log.d(TAG, "1");

            int i = (int) (0.65D * f);

            this.mHeaderImage.scrollTo(0, -i);

        } else if (this.mHeaderImage.getScrollY() != 0) {
            Log.d(TAG, "2");

            this.mHeaderImage.scrollTo(0, 0);
        }

        if (this.mOnScrollListener != null) {
            this.mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (this.mOnScrollListener != null) {
            this.mOnScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();

        Log.d(TAG, "onTouchEvent() " + (0xFF & action));

        switch (0xFF & action) {
            case MotionEvent.ACTION_DOWN:

                if (!this.mScalingRunnalable.mIsFinished) {
                    this.mScalingRunnalable.abortAnimation();
                }

                this.mLastMotionY = event.getY();
                this.mActivePointerId = event.getPointerId(0); // 触点的id值
                this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);
                this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);

                Log.d(TAG, "ACTION_DOWN  mLastMotionY=" + mLastMotionY + ",mActivePointerId=" + mActivePointerId + ",mMaxScale=" + mMaxScale + ",mLastScale=" + mLastScale);

                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "ACTION_MOVE mActivePointerId=" + this.mActivePointerId);

                int pointerIndex = event.findPointerIndex(this.mActivePointerId); //通过触点的id值得到触点的索引值

                if (pointerIndex == -1) {
                    Log.e("PullToZoomListView", "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent");

                } else {
                    float y = event.getY(pointerIndex);

                    if (this.mLastMotionY == -1.0F) { // 应该是保证不触发ACTION_DOWN，程序也能正常运行
                        this.mLastMotionY = y;
                    }

                    if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {
                        ViewGroup.LayoutParams params = this.mHeaderContainer.getLayoutParams();

                        float f = ((y - this.mLastMotionY + this.mHeaderContainer.getBottom()) / this.mHeaderHeight - this.mLastScale) / 2.0F + this.mLastScale;

                        if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)) {
                            params.height = this.mHeaderHeight;

                            this.mHeaderContainer.setLayoutParams(params);

                            return super.onTouchEvent(event);
                        }

                        this.mLastScale = Math.min(Math.max(f, 1.0F), this.mMaxScale);

                        params.height = ((int) (this.mHeaderHeight * this.mLastScale));
                        if (params.height < this.mScreenHeight) {
                            this.mHeaderContainer.setLayoutParams(params);
                        }

                        this.mLastMotionY = y;

                        return true;
                    }
                    this.mLastMotionY = y;
                }
                break;
            case MotionEvent.ACTION_UP:

                reset();
                endScraling();

                break;
            case MotionEvent.ACTION_CANCEL:

                int i = event.getActionIndex();

                this.mLastMotionY = event.getY(i);
                this.mActivePointerId = event.getPointerId(i);

                break;
            case MotionEvent.ACTION_POINTER_DOWN:

                onSecondaryPointerUp(event);

                this.mLastMotionY = event.getY(event.findPointerIndex(this.mActivePointerId));

                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    public void setHeaderViewSize(int width, int height) {
        Object obj = this.mHeaderContainer.getLayoutParams();
        if (obj == null) {
            obj = new LayoutParams(width, height);
        }
        ((ViewGroup.LayoutParams) obj).width = width;
        ((ViewGroup.LayoutParams) obj).height = height;
        this.mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams) obj);
        this.mHeaderHeight = height;
    }

    public void setOnScrollListener(OnScrollListener listener) {
        this.mOnScrollListener = listener;
    }

    public void setShadow(int resId) {
        this.mShadow.setBackgroundResource(resId);
    }

    public ImageView getHeaderView() {
        return this.mHeaderImage;
    }

    class ScalingRunnalable implements Runnable {
        long mDuration;
        boolean mIsFinished = true;
        float mScale;
        long  mStartTime;

        ScalingRunnalable() {}

        public void abortAnimation() {
            this.mIsFinished = true;
        }

        public boolean isFinished() {
            return this.mIsFinished;
        }

        public void run() {
            if ((!this.mIsFinished) && (this.mScale > 1.0D)) {

                float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime) / (float) this.mDuration;
                float f2 = this.mScale - (this.mScale - 1.0F) * PullToZoomListView.sInterpolator.getInterpolation(f1);

                ViewGroup.LayoutParams params = PullToZoomListView.this.mHeaderContainer.getLayoutParams();

                if (f2 > 1.0F) {
                    Log.d(TAG, "f2>1.0");

                    params.height = PullToZoomListView.this.mHeaderHeight;
                    params.height = ((int) (f2 * PullToZoomListView.this.mHeaderHeight));

                    PullToZoomListView.this.mHeaderContainer.setLayoutParams(params);
                    PullToZoomListView.this.post(this);

                    return;
                }
                this.mIsFinished = true;
            }
        }

        public void startAnimation(long duration) {

            this.mStartTime = SystemClock.currentThreadTimeMillis();
            this.mDuration = duration;
            this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer.getBottom()) / PullToZoomListView.this.mHeaderHeight);
            this.mIsFinished = false;

            PullToZoomListView.this.post(this);
        }
    }
}
